package dynaspring;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.InvalidPropertiesFormatException;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;

/**
 * Configuration class used by a DynaSpring context. It is an enhanced Properties object which manages a dynamic stack of Properties:
 * you can push and pop Properties at any time, and property values will always be resolved starting from the top of the stack, falling back
 * to the second element if the property is not found, and so on until the bottom of the stack. The bottom is the system Properties object and
 * cannot be popped.
 * @author Alessio Stalla
 *
 */
public class Configuration extends Properties implements ISuperProperties {

	private static final long serialVersionUID = 1L;
	private SuperProperties delegate;
	private static final Log log = LogFactory.getLog(Configuration.class);
	private Map<String, Object> variables = new HashMap<String, Object>();
	
	public Configuration() {
		this(System.getProperties());
	}
	
	public Configuration(Properties properties) {
		delegate = new SuperProperties(properties);
	}

	public SuperProperties pushConfiguration(String resourcePath, ResourceLoader resLoader) throws IOException {
		return pushConfiguration(resLoader.getResource(replacePlaceholders(resourcePath)));
	}
	
	public SuperProperties pushConfiguration(Resource r) throws IOException {
		log.info("Loading configuration from " + r);
		SuperProperties p = new SuperProperties();
		p.load(r.getInputStream());
		return pushConfiguration(p);
	}
	
	public SuperProperties pushConfiguration(Properties p) {
		SuperProperties newProperties = new SuperProperties(this.delegate);
		Enumeration<?> en = p.propertyNames();
		while(en.hasMoreElements()) {
			Object o = en.nextElement();
			newProperties.put(o, p.get(o));
		}
		this.delegate = newProperties;
		return this.delegate;
	}
	
	public SuperProperties popConfiguration() {
		Properties parent = this.delegate.getParent();
		if(parent instanceof SuperProperties) {
			this.delegate = (SuperProperties) parent;
			return this.delegate;
		}
		return null;
	}
	
	public void loadConfiguration(ResourceLoader resLoader, Object... configuration) {
        for(Object o : configuration) {
        	try {
	        	if(o instanceof Properties) {
	        		pushConfiguration((Properties) o);
	        	} else if(o instanceof Resource) {
					pushConfiguration((Resource) o);
	        	} else if(o instanceof String) {
        			pushConfiguration((String) o, resLoader);
	        	} else {
	        		throw new IllegalArgumentException("Cannot read properties from " + o);
	        	}
        	} catch (IOException e) {
				throw new RuntimeException("Error reading properties from " + o, e);
			}
        }
    }
	
	public void reset() {
		delegate = new SuperProperties(System.getProperties());
	}

	public Map<String, Object> getVariables() {
		return variables;
	}
	
	//Automatically generated delegate methods
	public void clear() {
		delegate.clear();
	}

	public boolean contains(Object value) {
		return delegate.contains(value);
	}

	public boolean containsKey(Object key) {
		return delegate.containsKey(key);
	}

	public boolean containsValue(Object value) {
		return delegate.containsValue(value);
	}

	public Enumeration<Object> elements() {
		return delegate.elements();
	}

	public Set<java.util.Map.Entry<Object, Object>> entrySet() {
		return delegate.entrySet();
	}

	public Object get(Object key) {
		return delegate.get(key);
	}

	public String getProperty(String key, String defaultValue) {
		return delegate.getProperty(key, defaultValue);
	}

	public String getProperty(String key) {
		return delegate.getProperty(key);
	}

	public boolean isEmpty() {
		return delegate.isEmpty();
	}

	public Enumeration<Object> keys() {
		return delegate.keys();
	}

	public Set<Object> keySet() {
		return delegate.keySet();
	}

	public void list(PrintStream out) {
		delegate.list(out);
	}

	public void list(PrintWriter out) {
		delegate.list(out);
	}

	public void load(InputStream inStream) throws IOException {
		delegate.load(inStream);
	}

	public void load(Reader reader) throws IOException {
		delegate.load(reader);
	}

	public void loadFromXML(InputStream in) throws IOException,
			InvalidPropertiesFormatException {
		delegate.loadFromXML(in);
	}

	public Enumeration<?> propertyNames() {
		return delegate.propertyNames();
	}

	public Object put(Object key, Object value) {
		return delegate.put(key, value);
	}

	public void putAll(Map<? extends Object, ? extends Object> t) {
		delegate.putAll(t);
	}

	public Object remove(Object key) {
		return delegate.remove(key);
	}

	@Deprecated
	public void save(OutputStream out, String comments) {
		delegate.save(out, comments);
	}

	public Object setProperty(String key, String value) {
		return delegate.setProperty(key, value);
	}

	public int size() {
		return delegate.size();
	}

	public void store(OutputStream out, String comments) throws IOException {
		delegate.store(out, comments);
	}

	public void store(Writer writer, String comments) throws IOException {
		delegate.store(writer, comments);
	}

	public void storeToXML(OutputStream os, String comment, String encoding)
			throws IOException {
		delegate.storeToXML(os, comment, encoding);
	}

	public void storeToXML(OutputStream os, String comment) throws IOException {
		delegate.storeToXML(os, comment);
	}

	public Set<String> stringPropertyNames() {
		return delegate.stringPropertyNames();
	}

	public String toString() {
		return delegate.toString();
	}

	public Collection<Object> values() {
		return delegate.values();
	}

	public String replacePlaceholders(String value) {
		return delegate.replacePlaceholders(value);
	}

	public String replacePlaceholders(String value, boolean ignoreUnresolvablePlaceholders) {
		return delegate.replacePlaceholders(value, ignoreUnresolvablePlaceholders);
	}

	@SuppressWarnings("rawtypes")
	public List getList(String name) {
		return delegate.getList(name);
	}

	@SuppressWarnings("rawtypes")
	public Map getMap(String name) {
		return delegate.getMap(name);
	}

	public String getRawProperty(String name, String defaultValue) {
		return delegate.getRawProperty(name, defaultValue);
	}
	
	
	
}
